#
# MODSData.py
#
# Provides data model and custom properties for use by eLibrary using
# Google's DataStore. Supports current version of Library of Congress's MODS
# <http://www.loc.gov/standards/mods/>.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Copyright 2009,2010- Jeremy Nelson, Tomichi Informatics
#
import datetime,os
from basedata import *

from google.appengine.ext import db
from google.appengine.api import users

#=============================================================================#
# MODS Properties                                                             #
#=============================================================================#
class geographicCode(object):
    """"geographicCode" includes a geographic area code associated with a 
       resource.

       <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#geographicCode>
    """
    authority_dict = {None:0,
                      "marcgac":1,
                      "marccountry":2,
                      "iso3166":3}

    def __init__(self,
                 authority=None,
                 code=None):
        if geographicCode.authority_dict.has_key(authority):
            self.authority=geographicCode.authority_dict[authority]
        else:
            raise db.BadValueError('geographicCode authority %s not found '
                                   'in values: %s' %
                                   (authority,geographicCode.authority_dict))
        if not code:
            raise db.BadValueError('geographicCode code must have a value')
        else:
            self.code = code
        self.reverse_authority = dict()
        for k,v in geographicCode.authority_dict.iteritems():
            self.reverse_authority[v] = k

    def has_authority(self):
        return self.authority > 0

    def get_authority(self):
        if self.authority:
            return self.reverse_authority[self.authority]
        else:
            return None

    def __str__(self):
        return self.code

    def __not__(self):
        return (not (self.has_authority() or
                     self.code))

class geographicCodeProperty(db.Property):
    
    data_type = geographicCode

    def get_value_for_datastore(self,model_instance):
        geo_code = super(geographicCodeProperty,
                         self).get_value_for_datastore(model_instance)
        if geo_code:
            return [geo_code.authority,geo_code.code]
        else:
            return None

    
    def make_value_from_datastore(self,value):
        if value is None:
            return value
        authority = value[0]
        code = value[1]
        geo_code = geographicCode(code=code)
        geo_code.authority = authority
        return geo_code

    
    def validate(self,value):
        if value is not None and not isinstance(value,geographicCode):
            raise db.BadValueError('Property %s must be convertible '
                                   'to a geographicCode instance (%s)' %
                                   (self.name,value))
        return super(geographicCodeProperty,self).validate(value)

    def empty(self,value):
        return not value   

#=============================================================================#
# MODS Models                                                                 #
#=============================================================================#
class abstract(BaseModel):
    '''a summary of the content of the resource.

      <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#abstract>
    '''
    # Difference between descriptive and informative located at 
    # <http://writing.colostate.edu/guides/documents/abstract/pop2c.cfm>
    type = db.StringProperty(required=False,
                             choices=(["descriptive",
                                       "informative"]))
    value = db.TextProperty()
    display_label = db.StringProperty()
    xlink = db.LinkProperty()

class coordinates(db.Model):
    '''Supporting model for cartographics model'''
    coordinate = db.GeoPtProperty()
    previous_coordinate = db.SelfReferenceProperty()
    free_text = db.StringProperty() # Stores info when specific coordinates
                                    # cannot be parsed

class cartographics(BaseModel):
    '''"cartographics" includes cartographic data indicating spatial coverage.

      <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#cartographics>
    '''
    coordinates = db.ListProperty(db.Key,default=None)
    scale = db.StringProperty()
    projection = db.StringProperty()

    def get_coordinates(self):
        return self.get_collection(property_name="coordinates")

class classification(BaseModel):
    '''A designation applied to a resource that indicates the subject by 
       applying a formal system of coding and organizing resources 
       according to subject areas.

       <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#classification>
       <http://www.loc.gov/marc/sourcecode/classification/classificationsource.html>
    '''
    authority = db.StringProperty(required=True,
                                  choices=(["accs",
                                            "anscr",
                                            "ardocs",
                                            "azdocs",
                                            "blsrissc",
                                            "cacodoc",
                                            "cacodoc",
                                            "cadocs",
                                            "clutscny",
                                            "codocs",
                                            "cstud",
                                            "ddc",
                                            "fcps",
                                            "fldocs",
                                            "gadocs",
                                            "iadocs",
                                            "inspec",
                                            "ksdocs",
                                            "kssb",
                                            "laclaw",
                                            "ladocs",
                                            "lcc",
                                            "midocs",
                                            "mmlcc",
                                            "modocs",
                                            "moys",
                                            "msc",
                                            "msdocs",
                                            "naics",
                                            "nasasscg",
                                            "nbdocs",
                                            "ncdocs",
                                            "nicem",
                                            "njb",
                                            "nlm",
                                            "nmdocs",
                                            "nvdocs",
                                            "nydocs",
                                            "ohdocs",
                                            "okdocs",
                                            "ordocs",
                                            "padocs",
                                            "pssppbkj",
                                            "ridocs",
                                            "rswk",
                                            "rubbk",
                                            "rubbkd",
                                            "rubbkk",
                                            "rubbkm",
                                            "rubbkmv",
                                            "rubbkn",
                                            "rubbknp",
                                            "rubbko",
                                            "rueskl",
                                            "rugasnti",
                                            "scdocs",
                                            "sddocs",
                                            "sudocs",
                                            "swank",
                                            "txdocs",
                                            "udc",
                                            "undocs",
                                            "upsylon",
                                            "usgslcs",
                                            "utdocs",
                                            "vsiso",
                                            "wadocs",
                                            "widocs",
                                            "wydocs",
                                            "z"]))
    call_number = db.StringProperty(required=True)
    designation = db.StringProperty(choices=["standard","optional"])
    item_number = db.StringProperty()
    edition = db.StringProperty()

    def put(self):
        if not self.display_form:
            if self.item_number:
                self.display_form = "%s %s" % (self.call_number,
                                               self.item_number)
            else:
                self.display_form = self.call_number
            
        return db.Model.put(self)

class detail(db.Model):
    ''' Contains numbering and type of designation of the part in relation to
        the host/parent item in which a host item resides.

        <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#detail>
    '''
    type = db.StringProperty(required=False,
                             choices=(["part",
                                       "volume",
                                       "issue",
                                       "chapter",
                                       "section",
                                       "paragraph",
                                       "track",
                                       "page number"]))
    caption = db.StringProperty()
    level = db.StringProperty()
    number = db.FloatProperty()
    order = db.StringProperty()
    other_id = db.StringProperty()
    title = db.ReferenceProperty()

class form(BaseModel):
    '''
     A designation of a particular physical presentation of a resource.
    <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#form>
    '''
    marcform = ["braille",
                "electronic",
                "microfiche",
                "microfilm",
                "print",
                "large print"]
    marcsmd = ["chip cartridge",
               "computer optical disc cartridge",
               "magnetic disk",
               "magneto-optical disc",
               "optical disc",
               "remote",
               "tape cartridge",
               "tape cassette",
               "tape reel",
               "celestial globe",
               "earth moon globe",
               "planetary or lunar globe",
               "terrestrial globe",
               "atlas",
               "diagram",
               "map",
               "model",
               "profile",
               "remote-sensing image",
               "section",
               "view",
               "aperture card",
               "microfiche",
               "microfiche cassette",
               "microfilm cartridge",
               "microfilm cassette",
               "microfilm reel",
               "microopaque",
               "film cartridge",
               "film cassette",
               "film reel",
               "chart",
               "collage",
               "drawing",
               "flash card",
               "painting",
               "photomechanical print",
               "photonegative",
               "photoprint",
               "picture",
               "print",
               "technical drawing",
               "filmslip",
               "filmstrip cartridge",
               "filmstrip roll",
               "other filmstrip type",
               "slide",
               "transparency",
               "cylinder",
               "roll",
               "sound cartridge",
               "sound cassette",
               "sound disc",
               "sound-tape reel",
               "sound-track film",
               "wire recording",
               "braille",
               "combination",
               "moon",
               "tactile, with no writing system",
               "large print",
               "regular print",
               "text in looseleaf binder",
               "videocartridge",
               "videocassette",
               "videodisc",
               "videoreel"]
    authority = db.StringProperty(required=True,
                                  choices=(["gmd",
                                            "marcform",
                                            "marcsmd",
                                            "freeform"]))
    value = db.StringProperty(required=True)

    def put(self):
        if self.authority == 'marcform':
            try:
                self.marcform.index(self.value)
            except ValueError:
                raise db.BadValueError('MODSData form MARC form %s '
                                       'not found' % self.value)
        if self.authority == 'marcsmd':
            try:
                self.marcsmd.index(self.value)
            except ValueError:
                raise db.BadValueError('MODSData form MARC smd authority'
                                       ' form %s value not found' %
                                       self.value)
        return db.Model.put(self)

class extent(BaseModel):
    ''' A statement of the number and specific material of the units of
        the resource that express physical extent. Also, includes the extent
        for part and RDA extent vocabularies.
        <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#extent>
        <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#related_extent>
        <http://www.rdaonline.org/Manifestation_6_1_09-1.pdf> pgs. 6-7
    '''
    unit = db.StringProperty()
    start = db.StringProperty()
    end = db.StringProperty()
    total = db.StringProperty()
    listing = db.StringProperty()
    general = db.StringListProperty()
    image_extent = db.StringProperty(required=False,
                                     choices=(["activity card(s)",
                                               "chart(s)",
                                               "collage(s)",
                                               "drawing(s)",
                                               "flash card(s)",
                                               "icon(s)",
                                               "painting(s)",
                                               "photograph(s)",
                                               "picture(s)",
                                               "postcard(s)",
                                               "poster(s)",
                                               "print(s)",
                                               "radiograph(s)",
                                               "study print(s)",
                                               "technical drawing(s)",
                                               "wall chart(s)"]))
    is_illustrated = db.BooleanProperty()
    # Set boolean for image, text, and 3-D object extents when plural
    is_plural = db.BooleanProperty() 
    text_extent = db.StringProperty(required=False,
                                    choices=(["approximately",
                                              "case",
                                              "column",
                                              "folded",
                                              "incomplete",
                                              "in various foliations",
                                              "in various numberings",
                                              "in various pagings",
                                              "leaf",
                                              "page",
                                              "portfolio",
                                              "sheet",
                                              "unnumbered pages",
                                              "unnumbered sequence of pages",
                                              "volume",
                                              "volume (loose-leaf)"]))
    threeD_extent = db.StringProperty(required=False,
                                      choices=(["coin",
                                                "diorama",
                                                "exhibit",
                                                "game",
                                                "jigsaw puzzle",
                                                "medal",
                                                "mock-up",
                                                "model",
                                                "sculpture",
                                                "specimen",
                                                "toy"]))
                                    
    
class genre(BaseModel):
    """A term(s) that designates a category characterizing a particular style,
       form, or content, such as artistic, musical, literary composition, etc.
       <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#genre>

       authority choices come from:
       <http://www.loc.gov/marc/sourcecode/genre/genresource.html>

       value choices for marcgt comes from:
       <http://www.loc.gov/marc/sourcecode/genre/genrelist.html>

       For authority != martcgt, alt_value must be filled
    """
    authority = db.StringProperty(required=False,
                                  choices=(["aat",
                                            "amg",
                                            "bgtchm",
                                            "bt",
                                            "dct",
                                            "ericd",
                                            "estc",
                                            "fast",
                                            "-ftamc",
                                            "gem",
                                            "gmgpc",
                                            "gsafd",
                                            "gtlm",
                                            "ilot",
                                            "lcsh",
                                            "lcshac",
                                            "lctgm",
                                            "local",
                                            "marcgt",
                                            "mesh",
                                            "migfg",
                                            "mim",
                                            "ngl",
                                            "nimafc",
                                            "nmc",
                                            "radfg",
                                            "rbbin",
                                            "rbgenr",
                                            "rbpap",
                                            "rbpri",
                                            "rbprov",
                                            "rbpub",
                                            "rbtyp",
                                            "sao",
                                            "tsht"]))
    value = db.StringProperty(required=False,
                              choices=(["abstract or summary",
                                        "art original",
                                        "art reproduction",
                                        "article",
                                        "atlas",
                                        "autobiography",
                                        "bibliography",
                                        "biography",
                                        "book",
                                        "calendar",
                                        "catalog",
                                        "chart",
                                        "comic or graphic novel",
                                        "comic strip",
                                        "contains biographical information",
                                        "collective biography",
                                        "conference publication",
                                        "database",
                                        "dictionary",
                                        "diorama",
                                        "directory",
                                        "discography",
                                        "drama",
                                        "encyclopedia",
                                        "essay",
                                        "festschrift",
                                        "fiction",
                                        "filmography",
                                        "filmstrip",
                                        "finding aid",
                                        "flash card",
                                        "folktale",
                                        "font",
                                        "game",
                                        "government publication",
                                        "graphic",
                                        "globe",
                                        "handbook",
                                        "history",
                                        "hymnal",
                                        "humor, satire",
                                        "index",
                                        "instruction",
                                        "interview",
                                        "issue",
                                        "journal",
                                        "kit",
                                        "language instruction",
                                        "law report or digest",
                                        "legal article",
                                        "legal case and case notes",
                                        "legislation",
                                        "letter",
                                        "loose-leaf",
                                        "map",
                                        "memoir",
                                        "microscope slide",
                                        "mixed forms",
                                        "model",
                                        "motion picture",
                                        "multivolume monograph",
                                        "newspaper",
                                        "not fiction (general)",
                                        "novel",
                                        "numeric data",
                                        "offprint",
                                        "online system or service",
                                        "patent",
                                        "periodical",
                                        "picture",
                                        "poetry",
                                        "programmed text",
                                        "realia",
                                        "rehearsal",
                                        "remote sensing image",
                                        "reporting",
                                        "review",
                                        "script",
                                        "series",
                                        "short story",
                                        "slide",
                                        "sound",
                                        "speech",
                                        "standards or specifications",
                                        "statistics",
                                        "survey of literature",
                                        "technical drawing",
                                        "technical report",
                                        "thesis",
                                        "toy",
                                        "transparency",
                                        "treaty",
                                        "videorecording",
                                        "web site",
                                        "yearbook"]))
    alt_value = db.StringProperty()
    type = db.StringProperty()

    def get_value(self):
        if self.value and not self.alt_value:
            return self.value
        if not self.value and self.alt_value:
            return self.alt_value
        if self.value and self.alt_value:
            if self.authority == 'marcgt':
                return self.value
            else:
                return self.alt_value
        return None

    def put(self):
        if not self.value and not self.alt_value:
            raise db.BadValueError('genre must have a value or alternative value')
        if self.authority != 'marcgt' and not self.alt_value:
            raise db.BadValueError('genre.authority=%s must have alt_value' %
                                   self.authority)
        if self.authority == 'marcgt' and not self.value:
            raise db.BadValueError('genre marcgt authority must have a value')
        return db.Model.put(self)

class identifier(BaseModel):
    '''identifier contains a unique standard number or code that distinctively 
       identifies a resource. It includes manifestation, expression and work level 
       identifiers.

       <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#identifier>
    '''
    type = db.StringProperty(required=False,
                             choices=(['barcode',
                                       'doi',
                                       'hdl',
                                       'isbn',
                                       'ismn',
                                       'isrc',
                                       'issn',
                                       'issnl',
                                       'issue number',
                                       'istc',
                                       'lccn',
                                       'local',
                                       'matrix number',
                                       'music plate',
                                       'music publisher',
                                       'nucmc',
                                       'sici',
                                       'stock number',
                                       'upc',
                                       'videorecording identifier']))
    value = db.StringProperty(required=True)
    is_valid = db.BooleanProperty()
    status = db.StringProperty(required=False,
                               choices=(["incorrect",
                                         "cancelled"]))

class note(BaseModel):
    """General textual information about the physical description of a resource.
       Types are combined from both the General MODS note element and the 
       physicalDescription/notes elements.
       
       <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#note>
       <http://www.loc.gov/standards/mods/mods-notes.html>
    """
    type = db.StringProperty(required=False,
                             choices=(["accrual method",
                                       "accrual policy",
                                       "acquisition",
                                       "action",
                                       "additional physical form",
                                       "admin",
                                       "bibliographic history",
                                       "bibliography",
                                       "biographical",
                                       "cataloguer",
                                       "citation/reference",
                                       "condition",
                                       "conservation history",
                                       "content",
                                       "copyright",
                                       "creation/production credits",
                                       "date",
                                       "description",
                                       "exhibitions",
                                       "funding",
                                       "historical",
                                       "language",
                                       "marks",
                                       "medium",
                                       "organization",
                                       "original location",
                                       "original version",
                                       "ownership",
                                       "part text",
                                       "performers",
                                       "physical description",
                                       "physical details",
                                       "preferred citation",
                                       "presentation",
                                       "publications",
                                       "production",
                                       "reproduction",
                                       "restriction",
                                       "script",
                                       "source characteristics",
                                       "source dimensions",
                                       "source identifier",
                                       "source note",
                                       "source type",
                                       "statement of responsibility",
                                       "subject completeness",
                                       "support",
                                       "system details",
                                       "technique",
                                       "thesis",
                                       "venue",
                                       "version identification"]))
    value = db.TextProperty()
    authority = db.ListProperty(db.Key,
                                default=None)
    xlink = db.LinkProperty()

class hierarchicalGeographic(BaseModel):
    """A geographic name given in a hierarchical form relating to the resource.

       <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#hierarchicalGeographic>
    """
    type = db.StringProperty(required=True,
                             choices=(["continent",
                                       "country",
                                       "province",
                                       "region",
                                       "state",
                                       "territory",
                                       "county",
                                       "city",
                                       "citySection",
                                       "island",
                                       "area",
                                       "extraterrestrialArea",
                                       "unknown"]))
    value = db.StringProperty(required=True)
    contained_in = db.SelfReferenceProperty()
    cartographics = db.ListProperty(db.Key,default=None)
    code = geographicCodeProperty()
    abbreviations = db.StringListProperty()

    def __str__(self):
        return self.value

class holdings(db.Model):
    '''Holdings information consists of copy-specific information
       about the resource, information about the specific location
       within an institution, version information, and other information
       that is peculiar to the institution. Can also include additional
       information to support holdingExternal.

      <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#holdingSimple>
    '''
    electronicLocator = db.ReferenceProperty()
    enumerationAndChronology = db.StringProperty()
    enumUnitType = db.StringProperty()
    form = db.ReferenceProperty(form,
                                collection_name="holdings_form")
    note = db.ReferenceProperty(note,
                                collection_name="holdings_note")
    shelfLocator = db.StringProperty()
    subLocation = db.StringProperty()
    
class location(BaseModel):
    ''' Identifies the institution or repository holding the resource,
        or a remote location in the form of a URL where it is available.

    <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#location>
    '''
    authority = db.StringProperty(required=False,
                                  choices=(["marcorg",
                                            "oclcorg"]))
    displayLabel = db.StringProperty()
    free_text = db.StringProperty()
    holdings = db.ListProperty(db.Key,
                               default=None)
    note = db.TextProperty()
    geographic_location = db.ReferenceProperty()
    url = db.ReferenceProperty(collection_name="location_url")
    xlink = db.LinkProperty()

    def get_holdings(self):
        return self.get_collection(property_name="holdings")

class languageTerm(BaseModel):
    '''"languageTerm" contains the language(s) of the content of the resource.
       It may be expressed in textual or coded form. If in coded form, the 
       source of the code is contained in the value of the authority attribute. 
       If no authority is given, it is assumed that the content is textual. 
       <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#languageTerm>
    '''
    text = db.StringProperty()
    iso6392b = db.StringProperty() 
    rfc3066 = db.StringProperty()
    iso6393 = db.StringProperty()
    rfc4646 = db.StringProperty()

    def put(self):
        if not self.display_form:
            self.display_form = self.text
        return db.Model.put(self)

class recordIdentifier(BaseModel):
    """ recordIdentifier contains the system control number assigned by the
        organization creating, using, or distributing the record.
        <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#recordidentifier>
    """
    authority = db.ReferenceProperty()
    is_valid = db.BooleanProperty(default=True)
    value = db.StringProperty()

class recordInfo(BaseModel):
    """ recordInfo contains information about imported metadata records
        <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#recordInfo>
    """
    associated_entities = db.ListProperty(db.Key,
                                          default=None) # Entities associated with imported record
    cataloged_in_language = db.ReferenceProperty(collection_name="record_lang")
    changed_date = BaseDateProperty()
    creation_date = BaseDateProperty()
    description = db.ReferenceProperty(note,
                                       collection_name="record_description")
    modifying_agencies = db.ListProperty(db.Key,
                                         default=None)
    origin = db.StringListProperty()
    org_cataloging_agency = db.ReferenceProperty(\
        collection_name="original_cat_agency")
    record_identifiers = db.ListProperty(db.Key,
                                         default=None)
    transcribing_agency = db.ReferenceProperty(\
        collection_name="transcribing_cat_agency")


    def get_associated_entities(self):
        """ method returns list of entities that are associated with the
        importation of a record from an external system into eCataloger."""
        return self.get_collection(property_name="associated_entities")

    def get_identifiers(self):
        """ method returns list of recordIdentifier entities """
        return self.get_collection(property_name="record_identifiers")

    def get_modifying_agencies(self):
        """ method returns list of modifying agencies entities """
        return self.get_collection(property_name="modifying_agencies")

    
class relatedItem(BaseModel):
    ''' relatedItem provides an association between an existing eCataloger
        entity and other eCataloger entities.

        <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#relateditem>
    '''
    relationship = db.StringProperty(required=True,
                                     choices=(["preceding",
                                               "succeeding",
                                               "original",
                                               "host",
                                               "constituent",
                                               "series",
                                               "otherVersion",
                                               "otherFormat",
                                               "isReferencedBy"]))
    source = db.ReferenceProperty()
    targets = db.ListProperty(db.Key,
                              default=None)

    def get_targets(self):
        return self.get_collection(property_name="targets")
                                               
 
class role(BaseModel):
    ''' role term(s) that designates the relationship (role) of the entity
        recorded in name in relation to the resource.
        <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#role>

        Type choices come from:
        <http://www.loc.gov/marc/sourcecode/relator/relatorlist.html>
    '''
    code_dict = {"acp":"art copyist",
                 "act":"actor",
                 "adp":"adapter",
                 "aft":"author of afterword, colophon, etc.",
                 "anl":"analyst",
                 "anm":"animator",
                 "ann":"annotator",
                 "ant":"bibliographic antecedent",
                 "app":"applicant",
                 "aqt":"author in quotations or text abstracts",
                 "arc":"architect",
                 "ard":"artistic director",
                 "arr":"arranger",
                 "art":"artist",
                 "asg":"assignee",
                 "asn":"associated name",
                 "att":"attributed name",
                 "auc":"auctioneer",
                 "aud":"author of dialog",
                 "aui":"author of introduction",
                 "aus":"author of screenplay",
                 "aut":"author",
                 "bdd":"binding designer",
                 "bjd":"bookjacket designer",
                 "bkd":"book designer",
                 "bkp":"book producer",
                 "bnd":"binder",
                 "bpd":"bookplate designer",
                 "bsl":"bookseller",
                 "ccp":"conceptor",
                 "chr":"choreographer",
                 "clb":"collaborator",
                 "cli":"client",
                 "cll":"calligrapher",
                 "clt":"collotyper",
                 "cmm":"commentator",
                 "cmp":"composer",
                 "cmt":"compositor",
                 "cnd":"conductor",
                 "cng":"cinematographer",
                 "cns":"censor",
                 "coe":"contestant-appellee",
                 "col":"collector",
                 "com":"compiler",
                 "cos":"contestant",
                 "cot":"contestant-appellant",
                 "cov":"cover designer",
                 "cpc":"copyright claimant",
                 "cpe":"complainant-appellee",
                 "cph":"copyright holder",
                 "cpl":"complainant",
                 "cpt":"complainant-appellant",
                 "cre":"creator",
                 "crp":"correspondent",
                 "crr":"corrector",
                 "csl":"consultant",
                 "csp":"consultant to a project",
                 "cst":"costume designer",
                 "ctb":"contributor",
                 "cte":"contestee-appellee",
                 "ctg":"cartographer",
                 "ctr":"contractor",
                 "cts":"contestee",
                 "ctt":"contestee-appellant",
                 "cur":"curator",
                 "cwt":"commentator for written text",
                 "dfd":"defendant",
                 "dfe":"defendant-appellee",
                 "dft":"defendant-appellant",
                 "dgg":"degree grantor",
                 "dis":"dissertant",
                 "dln":"delineator",
                 "dnc":"dancer",
                 "dnr":"donor",
                 "dpc":"depicted",
                 "dpt":"depositor",
                 "drm":"draftsman",
                 "drt":"director",
                 "dsr":"designer",
                 "dst":"distributor",
                 "dtc":"data contributor",
                 "dte":"dedicatee",
                 "dtm":"data manager",
                 "dto":"dedicator",
                 "dub":"dubious author",
                 "edt":"editor",
                 "egr":"engraver",
                 "elg":"electrician",
                 "elt":"electrotyper",
                 "eng":"engineer",
                 "etr":"etcher",
                 "exp":"expert",
                 "fac":"facsimilist",
                 "fld":"field director",
                 "flm":"film editor",
                 "fmo":"former owner",
                 "fnd":"funder",
                 "fpy":"first party",
                 "frg":"forger",
                 "gis":"geographic information specialist",
                 "hnr":"honoree",
                 "hst":"host",
                 "ill":"illustrator",
                 "ilu":"illuminator",
                 "ins":"inscriber",
                 "inv":"inventor",
                 "itr":"instrumentalist",
                 "ive":"interviewee",
                 "ivr":"interviewer",
                 "lbr":"laboratory",
                 "lbt":"librettist",
                 "ldr":"laboratory director",
                 "led":"lead",
                 "lee":"libelee-appellee",
                 "lel":"libelee",
                 "len":"lender",
                 "let":"libelee-appellant",
                 "lgd":"lighting designer",
                 "lie":"libelant-appellee",
                 "lil":"libelant",
                 "lit":"libelant-appellant",
                 "lsa":"landscape architect",
                 "lse":"licensee",
                 "lso":"licensor",
                 "ltg":"lithographer",
                 "lyr":"lyricist",
                 "mcp":"music copyist",
                 "mdc":"metadata contact",
                 "mfr":"manufacturer",
                 "mod":"moderator",
                 "mon":"monitor",
                 "mrk":"markup editor",
                 "msd":"musical director",
                 "mte":"metal-engraver",
                 "mus":"musician",
                 "nrt":"narrator",
                 "opn":"opponent",
                 "org":"originator",
                 "orm":"organizer of meeting",
                 "oth":"other",
                 "own":"owner",
                 "pat":"patron",
                 "pbd":"publishing director",
                 "pbl":"publisher",
                 "pdr":"project director",
                 "pfr":"proofreader",
                 "pht":"photographer",
                 "plt":"platemaker",
                 "pma":"permitting agency",
                 "pmn":"production manager",
                 "pop":"printer of plates",
                 "ppm":"papermaker",
                 "ppt":"puppeteer",
                 "prc":"process contact",
                 "prd":"production personnel",
                 "prf":"performer",
                 "prg":"programmer",
                 "prm":"printmaker",
                 "pro":"producer",
                 "prt":"printer",
                 "pta":"patent applicant",
                 "pte":"plaintiff-appellee",
                 "ptf":"plaintiff",
                 "pth":"patent holder",
                 "ptt":"plaintiff-appellant",
                 "rbr":"rubricator",
                 "rce":"recording engineer",
                 "rcp":"recipient",
                 "red":"redactor",
                 "ren":"renderer",
                 "res":"researcher",
                 "rev":"reviewer",
                 "rps":"repository",
                 "rpt":"reporter",
                 "rpy":"responsible party",
                 "rse":"respondent-appellee",
                 "rsg":"restager",
                 "rsp":"respondent",
                 "rst":"respondent-appellant",
                 "rth":"research team head",
                 "rtm":"research team member",
                 "sad":"scientific advisor",
                 "sce":"scenarist",
                 "scl":"sculptor",
                 "scr":"scribe",
                 "sds":"sound designer",
                 "sec":"secretary",
                 "sgn":"signer",
                 "sht":"supporting host",
                 "sng":"singer",
                 "spk":"speaker",
                 "spn":"sponsor",
                 "spy":"second party",
                 "srv":"surveyor",
                 "std":"set designer",
                 "stl":"storyteller",
                 "stm":"stage manager",
                 "stn":"standards body",
                 "str":"stereotyper",
                 "tcd":"technical director",
                 "tch":"teacher",
                 "ths":"thesis advisor",
                 "trc":"transcriber",
                 "trl":"translator",
                 "tyd":"type designer",
                 "tyg":"typographer",
                 "vdg":"videographer",
                 "voc":"vocalist",
                 "wam":"writer of accompanying material",
                 "wdc":"woodcutter",
                 "wde":"wood-engraver",
                 "wit":"witness"}
    type = db.StringProperty()
    authority = db.StringProperty(default="marcrelator")
    code = db.StringProperty()

    def put(self):
        reverse_codes = dict()
        for k,v in self.code_dict.iteritems():
            reverse_codes[v] = k
        if self.code and not self.type:
            if self.code_dict.has_key(self.code):
                self.type = self.code_dict[self.code]
        if not reverse_codes.has_key(self.type) and \
           self.authority=='marcrelator':
            raise db.BadValueError("Value %s not found in marcrelator" %
                                   self.type)
        if not self.display_form:
            if self.type:
                self.display_form = self.type
            elif self.code:
                self.display_form = self.code
        return db.Model.put(self)
                              
    
class subject(BaseModel):
    """A term or phrase representing the primary topic(s) on which a work is
       focused.

    <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#subject>
    Authority codes come from:
    <http://www.loc.gov/marc/sourcecode/subject/subjectsource.html>
    """
    authority = db.StringProperty(required=True,
                                  choices=(["aass",
                                            "aat",
                                            "abne",
                                            "agrifors",
                                            "agrovoc",
                                            "agrovocf",
                                            "agrovocs",
                                            "allars",
                                            "amg",
                                            "apaist",
                                            "asft",
                                            "atla",
                                            "bella",
                                            "bhammf",
                                            "bhashe",
                                            "bidex",
                                            "blmlsh",
                                            "bt",
                                            "cabt",
                                            "cht",
                                            "ciesiniv",
                                            "cilla",
                                            "csahssa",
                                            "csalsct",
                                            "csapa",
                                            "csh",
                                            "cstud",
                                            "ddcrit",
                                            "dissao",
                                            "dit",
                                            "drama",
                                            "dtict",
                                            "ebfem",
                                            "eks",
                                            "ericd",
                                            "est",
                                            "fast",
                                            "fgtpcm",
                                            "fmesh",
                                            "francis",
                                            "gem",
                                            "georeft",
                                            "googlebooks",
                                            "helecon",
                                            "henn",
                                            "hlasstg",
                                            "idas",
                                            "iest",
                                            "ilot",
                                            "ilpt",
                                            "inist",
                                            "inspect",
                                            "ipat",
                                            "ipsp",
                                            "isis",
                                            "itoamc",
                                            "jlabsh",
                                            "kaa",
                                            "kaunokki",
                                            "kssbar",
                                            "kta",
                                            "ktpt",
                                            "ktta",
                                            "kupu",
                                            "larpcal",
                                            "lcsh",
                                            "lcshac",
                                            "lctgm",
                                            "lemb",
                                            "lnmmbr",
                                            "local",
                                            "ltcsh",
                                            "lua",
                                            "masa",
                                            "mesh",
                                            "mipfesd",
                                            "mmm",
                                            "mpirdes",
                                            "mtirdes",
                                            "musa",
                                            "nasat",
                                            "ndllsh",
                                            "nicem",
                                            "nimacsc",
                                            "ntcpsc",
                                            "ntcsd",
                                            "pascal",
                                            "peri",
                                            "pha",
                                            "poliscit",
                                            "popinte",
                                            "psychit",
                                            "qrma",
                                            "qrmak",
                                            "ram",
                                            "rasuqam",
                                            "rero",
                                            "rpe",
                                            "rswk",
                                            "rurkp",
                                            "sao",
                                            "scgdst",
                                            "scisshl",
                                            "sears",
                                            "she",
                                            "sigle",
                                            "sk",
                                            "slem",
                                            "sosa",
                                            "swd",
                                            "taika",
                                            "taxhs",
                                            "test",
                                            "tgn",
                                            "tlka",
                                            "tlsh",
                                            "trt",
                                            "trtsa",
                                            "tsht",
                                            "ttka",
                                            "unbisn",
                                            "unbist",
                                            "unescot",
                                            "usaidt",
                                            "watrest",
                                            "wot",
                                            "wpicsh",
                                            "ysa"]))
    topics = db.StringListProperty(default=None)
    temporal = db.ListProperty(db.Key,
                               default=None)
    title = db.ReferenceProperty()
    events = db.ListProperty(db.Key,default=None)
    people = db.ListProperty(db.Key,default=None)
    organizations = db.ListProperty(db.Key,default=None)
    genre = db.ListProperty(db.Key,default=None)
    geographicCode = geographicCodeProperty()
    cartographics = db.ListProperty(db.Key,default=None)
    geographic = db.ListProperty(db.Key,default=None)
    occupation = db.StringProperty()

    def get_topics(self):
        output = list()
        for topic in self.topics:
            output.append(db.Category(topic))
        return output

    def get_geography(self):
        return self.get_collection(property_name="geographic")

    def get_genre(self):
        return self.get_collection(property_name="genre")

    def get_temporal(self):
        return self.get_collection(property_name="temporal")

    def get_cartographics(self):
        return self.get_collection(property_name="cartographics")

    def get_people(self):
        return self.get_collection(property_name="people")

    def get_organizations(self):
        return self.get_collection(property_name="organizations")

    def get_events(self):
        return self.get_collection(property_name="events")

    def topic(self):
        if len(self.topics) == 1:
            return db.Category(self.topics[0])
        else:
            return None

    def put(self):
        if not self.display_form:
            if self.topic():
                self.display_form = self.topic()
            elif len(self.topics) > 1:
                self.display_form = ''
                for topic in self.topics:
                    self.display_form += "%s " % topic
                self.display_form.strip()
        return db.Model.put(self)

class tableOfContents(BaseModel):
    """  A description of the contents of a resource.
         <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#tableofcontents>
    """
    type = db.StringProperty()
    contents = db.StringListProperty()
    xlink = db.LinkProperty()
    
class titleInfo(BaseModel):
    """A word, phrase, character, or group of characters, normally
       appearing in a resource, that names it or the work contained in it.
       <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#titleInfo>

       displayLabel is the entire title string that is normally used for display.
       It is a composite of the title and nonSort fields.

       nonSort usually contains "A,An,The" terms that begin a title. 

       title field stores the sorted title without the nonSort prefix. 
       """
    type = db.StringProperty(required=True,
                             choices=(["abbreviated",
                                       "early varient",
                                       "later varient",
                                       "translated",
                                       "alternative",
                                       "uniform"]))
    authority = db.StringProperty(required=False,
                                  choices=(["naf",
                                            "sanb"]))
    is_preferred_title = db.BooleanProperty()
    subTitle = db.StringProperty()
    partName = db.StringListProperty()
    partNumber = db.StringListProperty()
    nonSort = db.StringProperty()
    title = db.StringProperty(required=True)

    @classmethod
    def filter_prefix(cls,property_name,prefix):
        query = cls.all()
        query.filter("%s >= " % property_name,
                     u"%s" % prefix)
        query.filter("%s < " % property_name,
                     u"%s\xEF\xBF\xBD" % prefix)
        return query
    

class url(BaseModel):
    """The Uniform Resource Location of the resource

       <http://www.loc.gov/standards/mods/v3/mods-userguide-elements.html#url>
    """
    dateLastAccessed = db.DateTimeProperty()
    is_valid = db.BooleanProperty()
    note = db.TextProperty()
    access = db.StringProperty(required=False,
                               choices=(["preview",
                                         "proxy",
                                         "raw object",
                                         "object in context"]))
    usage = db.StringProperty(default="primary display")
    value = db.LinkProperty(required=True)
